/**
 * 
 */
package org.softTank.BC.manager.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.log4j.Logger;
import org.softTank.BC.rs.bean.MessageTypeBean;
import org.softTank.BC.rs.bean.OperationBean;
import org.softTank.BC.rs.bean.PartBean;
import org.softTank.BC.rs.bean.PartnerLinkTypeBean;
import org.softTank.BC.rs.bean.RoleBean;
import org.softTank.BC.rs.bean.ServiceBean;
import org.softTank.BC.rs.bean.TBoolean;
import org.softTank.BC.ws.client.UddiQueryClient;
import org.softTank.wsdlParser.BpelDecorator;
import org.softTank.wsdlParser.BpelDecoratorImpl;
import org.softTank.wsdlParser.WsdlFactoryImpl;
import org.softTank.wsdlParser.WsdlParser;
import org.softTank.wsdlParser.bean.Definitions;
import org.softTank.wsdlParser.bean.Message;
import org.softTank.wsdlParser.bean.Operation;
import org.softTank.wsdlParser.bean.Part;
import org.softTank.wsdlParser.bean.PartnerLinkType;
import org.softTank.wsdlParser.bean.PortType;
import org.softTank.wsdlParser.bean.Role;

/**
 * @author Yuzone
 *
 */
public class ServiceParser {

	protected Logger logger;
	protected UddiQueryClient client;
	protected WsdlParser wsdlParser;
	public static Map<String, String> wsdlMap;
	public static Map<String, Definitions> definitionMap;
	static String DIR = "";

	public ServiceParser() {
		logger = Logger.getLogger(this.getClass());
		Properties p = new Properties();
		try {
			p.load(this.getClass().getClassLoader().getResourceAsStream(
					"application.properties"));
			DIR = p.get("wsdl_address").toString();
		} catch (IOException e) {
			logger
					.warn("Cannot find .properties file!(use default address now) "
							+ e.getMessage());
		}
		if (wsdlMap == null) {
			wsdlMap = new HashMap<String, String>();
		}
		if (definitionMap == null) {
			definitionMap = new HashMap<String, Definitions>();
		}
	}

	public List<ServiceBean> getServices(String category) {
		if (client == null) {
			client = new UddiQueryClient();
		}
		List<ServiceBean> sbl = new ArrayList<ServiceBean>();

		String result = client.queryByServiceType(category);
		if (result == null)
			return null;
		JSONArray services = JSONArray.fromObject(result);
		for (Object obj : services) {
			JSONObject service = JSONObject.fromObject(obj);
			ServiceBean sb = new ServiceBean();
			sb.setName(service.getString("name"));
			sb.setId(service.getString("id"));
			sb.setUrl(service.getString("url"));
			sbl.add(sb);
			wsdlMap.put(service.getString("id"), service.getString("url"));
		}

		return sbl;
	}

	public ServiceBean getService(String id) {
		if (wsdlParser == null) {
			String url = ServiceParser.wsdlMap.get(id);
			if (url == null) {
				logger.debug("url is null!");
				return null;
			}
			try {
				wsdlParser = new WsdlFactoryImpl().getWsdlParser(new URL(url));
			} catch (MalformedURLException e) {
				logger.warn("Cannot parse WSDL address!\n" + e.getMessage());
				return null;
			} catch (JAXBException e) {
				logger.error("Cannot parse the WSDL!\n" + e.getMessage());
				return null;
			}
		}
		Definitions def = wsdlParser.getDefinitions();
		if (def == null)
			return null;
		if (def.getPartnerLinkType() == null) {
			// Add partnerLinkType to WSDL file.
			BpelDecorator bd = new BpelDecoratorImpl();
			try {
				String wsdl = bd.decorateWSDL(new URL(ServiceParser.wsdlMap
						.get(id)));
				BufferedWriter out = new BufferedWriter(new FileWriter(DIR + id
						+ ".wsdl"));
				out.write(wsdl);
				out.close();
				def = new WsdlFactoryImpl().getWsdlParser(
						new File(DIR + id + ".wsdl"))
						.getDefinitions();
				ServiceParser.wsdlMap.put(id, DIR + id + ".wsdl");
			} catch (MalformedURLException e) {
				logger.warn("Cannot parse WSDL address!\n" + e.getMessage());
			} catch (IOException e) {
				logger.warn("Cannot save new wsdl!\n" + e.getMessage());
			} catch (JAXBException e) {
				e.printStackTrace();
			}
		}
		ServiceParser.definitionMap.put(id, def);

		ServiceBean sb = new ServiceBean();
		// sb.setPartnerLinkType(pltbList);
		sb.setId(id);
		sb.setOperation(getOperationBeans(def, id));

		return sb;
	}

	private List<OperationBean> getOperationBeans(Definitions def, String id) {
		List<OperationBean> obList = new ArrayList<OperationBean>();
		List<Operation> oList = def.getPortType().get(0).getOperation();
		if (oList != null) {
			for (Operation o : oList) {
				OperationBean ob = new OperationBean();
				ob.setId(id);
				ob.setHasInput(TBoolean.NO);
				ob.setHasOutput(TBoolean.NO);
				ob.setName(o.getName().getLocalPart());
				if (o.getInput() != null) {
					// ob.setInput(getMessageTypeBean(o.getInput().getMessage(),
					// def));
					ob.setHasInput(TBoolean.YES);
				}
				if (o.getOutput() != null) {
					// ob.setOutput(getMessageTypeBean(o.getOutput().getMessage(),
					// def));
					ob.setHasOutput(TBoolean.YES);
				}
				obList.add(ob);
			}
			return obList;
		}
		logger.debug("No operation found!");

		return null;
	}

	private List<PartnerLinkTypeBean> getPartnerLinkTypeBeans(Definitions def) {
		List<PartnerLinkType> pltList = def.getPartnerLinkType();
		if (pltList == null) {
			logger.debug("There is no PartnerLinkType defined in this WSDL!");
			return null;
		}
		List<PartnerLinkTypeBean> pltbList = new ArrayList<PartnerLinkTypeBean>();
		for (PartnerLinkType plt : pltList) {
			PartnerLinkTypeBean pltb = new PartnerLinkTypeBean();
			pltb.setName(plt.getName().getLocalPart());
			List<Role> rList = plt.getRole();
			if (rList == null) {
				logger
						.warn("There are errors in the definition of PartnerLinkType: "
								+ plt.getName());
				pltbList.add(pltb);
				continue;
			}
			List<RoleBean> rbList = new ArrayList<RoleBean>();
			for (Role r : rList) {
				RoleBean rb = new RoleBean();
				rb.setName(r.getName().getLocalPart());
				rb.setPortType(r.getPortType().getLocalPart());
				List<PortType> ptList = def.getPortType();
				if (ptList == null) {
					logger.debug("There is no PortType defined in this WSDL!");
					rbList.add(rb);
					continue;
				}
				List<OperationBean> obList = new ArrayList<OperationBean>();
				List<Operation> oList = null;
				for (PortType pt : ptList) {
					if (pt.getName().equals(r.getPortType())) {
						oList = pt.getOperation();
					}
				}
				if (oList == null) {
					logger.debug("No Operation definition found!");
					rbList.add(rb);
					continue;
				}
				for (Operation o : oList) {
					OperationBean ob = new OperationBean();
					ob.setName(o.getName().getLocalPart());
					if (o.getInput() != null) {
						ob.setInput(getMessageTypeBean(o.getInput()
								.getMessage(), def));
					}
					if (o.getOutput() != null) {
						ob.setOutput(getMessageTypeBean(o.getOutput()
								.getMessage(), def));
					}
					obList.add(ob);
				}
				rb.setOperation(obList);
				rbList.add(rb);
			}
			pltb.setRole(rbList);
			pltbList.add(pltb);
		}
		return pltbList;
	}

	private MessageTypeBean getMessageTypeBean(QName value, Definitions def) {
		List<Message> mList = def.getMessage();
		if (mList == null) {
			logger.debug("There is no Message definition in this WSDL!");
			return null;
		}
		Message msg = null;
		// FIXME message's name is different!
		for (Message m : mList) {
			if (value.getLocalPart().equalsIgnoreCase(
					m.getName().getLocalPart())) {
				msg = m;
				break;
			}
		}
		if (msg == null) {
			logger
					.debug("Referenced Message definition is not found in This WSDL!");
			return null;
		}
		MessageTypeBean mtb = new MessageTypeBean();
		mtb.setName(msg.getName().getLocalPart());
		mtb.setPart(getPartBeans(msg.getPart()));

		return mtb;
	}

	private List<PartBean> getPartBeans(List<Part> value) {
		if (value == null) {
			logger.debug("No Part definition in this Message element!");
			return null;
		}
		List<PartBean> pbList = new ArrayList<PartBean>();
		for (Part p : value) {
			PartBean pb = new PartBean();
			pb.setName(p.getName().getLocalPart());
			pb.setElement(p.getElement().getLocalPart());
			pbList.add(pb);
		}

		return pbList;
	}

	public UddiQueryClient getClient() {
		return client;
	}

	public void setClient(UddiQueryClient client) {
		this.client = client;
	}

	public void setWsdlParser(WsdlParser wsdlParser) {
		this.wsdlParser = wsdlParser;
	}
}
